﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using WinApp.Common;
using System.Threading;

namespace WinApp
{
    public partial class FormSocketServer : Form
    {
        private SocketListener mListener;

        private TcpListener mTcpListener;

        private UdpClient mUdpClient;

        private Dictionary<string, TcpConnection> mTcpClientDictionary;

        private Dictionary<string, DateTime> mUdpClientDictionary;

        public FormSocketServer()
        {
            InitializeComponent();
        }

        private void FormSocketTest_Load(object sender, EventArgs e)
        {
            listBoxAddressList.Items.Clear();
            listBoxClientList.Items.Clear();

            mTcpClientDictionary = new Dictionary<string, TcpConnection>();
            mUdpClientDictionary = new Dictionary<string, DateTime>();
        }

        private void buttonDnsLookup_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrEmpty(textBoxDns.Text))
            {
                return;
            }

            string hostName = textBoxDns.Text.Trim();
            IPHostEntry hostEntry = Dns.GetHostEntry(hostName);

            listBoxAddressList.Items.Clear();
            foreach (IPAddress ipAddress in hostEntry.AddressList)
            {
                listBoxAddressList.Items.Add(ipAddress);
            }
        }

        private void buttonStartSocketListener_Click(object sender, EventArgs e)
        {
            listBoxAddressList.Items.Clear();

            ThreadPool.QueueUserWorkItem((state) => {
                if (mListener == null)
                {
                    mListener = new SocketListener(Config.SocketServerIPAddress, Config.SocketServerPort, ProtocolType.Tcp);
                    mListener.ListenBacklog = Config.SocketServerListenBacklog;

                    mListener.ReceivedNoticeAction = (sc, receivedData) => {
                        PrintMessage(sc, receivedData);
                    };

                    mListener.SocketConnectionChanged = (connections) => {
                        RefreshClientList(connections);
                    };
                }                                

                try
                {
                    EnableButton(buttonStartSocketListener, false);

                    mListener.Start();
                }
                catch (Exception ex)
                {
                    EnableButton(buttonStartSocketListener, true);
                }
            });
        }

        private void EnableButton(Button button, bool enabled)
        {
            if (button.InvokeRequired)
            {
                button.Invoke((Action)(() => {
                    button.Enabled = enabled;
                }));
            }
            else
            {
                button.Enabled = enabled;
            }
        }

        private void RefreshClientList(ReadOnlyCollection<SocketConnection> connections)
        {
            if (listBoxClientList.InvokeRequired)
            {
                listBoxClientList.Invoke((Action)(() => {
                    listBoxClientList.Items.Clear();
                    foreach (SocketConnection sc in connections)
                    {
                        listBoxClientList.Items.Add(sc.Connection.RemoteEndPoint.ToString() + " " + sc.LastConnectTime + ": ");
                    }
                }));
            }
            else
            {
                listBoxClientList.Items.Clear();
                foreach (SocketConnection sc in connections)
                {
                    listBoxClientList.Items.Add(sc.Connection.RemoteEndPoint.ToString() + " " + sc.LastConnectTime + ": ");
                }
            }
        }

        private void PrintMessage(SocketConnection sc, string message)
        {
            string clientIP = sc.Connection.RemoteEndPoint.ToString();

            if (listBoxAddressList.InvokeRequired)
            {
                listBoxAddressList.Invoke((Action)(() => {                    
                    listBoxAddressList.Items.Add(clientIP + ": " + message);
                }));
            }
            else
            {
                listBoxAddressList.Items.Add(clientIP + ": " + message);
            }

            RefreshClientList(mListener.Connections);
        }

        private void buttonStopSocketListener_Click(object sender, EventArgs e)
        {
            StopSocketListener();
        }

        private void StopSocketListener()
        {
            try
            {
                if (mListener != null)
                {
                    mListener.Dispose();
                    mListener = null;
                }
            }
            finally
            {
                EnableButton(buttonStartSocketListener, true);
            }
        }

        private void FormSocketServer_FormClosing(object sender, FormClosingEventArgs e)
        {
            StopSocketListener();
        }

        private void buttonTerminateConnection_Click(object sender, EventArgs e)
        {
            if (listBoxClientList.SelectedIndex == -1)
            {
                return;
            }

            string[] data = listBoxClientList.Text.ToString().Split(' ');
            string clientIP = data[0];
            bool success = mListener.TerminateConnection(clientIP);

            if (success)
            {
                RefreshClientList(mListener.Connections);
            }
        }

        private void buttonStartTcpListener_Click(object sender, EventArgs e)
        {
            ThreadPool.QueueUserWorkItem((state) => {

                if (mTcpListener == null)
                {
                    mTcpListener = new TcpListener(IPAddress.Parse(Config.SocketServerIPAddress), Config.SocketServerPort);
                    mTcpListener.Start();

                    EnableButton(buttonStartTcpListener, false);
                }

                while (true)
                {
                    TcpClient tcpClient = mTcpListener.AcceptSocket();
                    
                    try
                    {
                        TcpConnection connection = new TcpConnection(tcpClient);
                        connection.LastConnectTime = DateTime.Now;

                        connection.OnReceived = (tc, data) => {
                            PrintReceivedData(tc, data);
                        };

                        Thread thread = new Thread((paramObj) => {
                            connection.HandleResponse();
                        });
                        thread.IsBackground = true;
                        thread.Name = tcpClient.Client.RemoteEndPoint.ToString();
                        thread.Start(connection.TcpClient);

                        mTcpClientDictionary.Add(thread.Name, connection);

                        //update client list
                        listBoxClientList.Invoke((Action)(() => {
                            listBoxClientList.Items.Clear();
                            foreach (var key in mTcpClientDictionary.Keys)
                            {
                                listBoxClientList.Items.Add(key + " " + mTcpClientDictionary[key].LastConnectTime.ToString("yyyy-MM-dd HH:mm:ss"));
                            }
                        }));
                    }
                    catch
                    {
                        continue;
                    }
                }
            });            
        }

        private void PrintReceivedData(TcpClient tcpClient, string data)
        {
            if (listBoxAddressList.InvokeRequired)
            {
                listBoxAddressList.Invoke((Action)(() => { 
                    string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                    string endPoint = tcpClient.Client.RemoteEndPoint.ToString();
                    listBoxAddressList.Items.Add(endPoint + " " + now + ": " + data);
                }));
            }
            else
            {

            }
        }

        private bool IsSocketConnected(Socket socket)
        {
            bool connectionNotClosedResetOrTerminated = !socket.Poll(1000, SelectMode.SelectRead);
            bool socketHasDataAvailableToRead = (socket.Available != 0);
            return (connectionNotClosedResetOrTerminated || socketHasDataAvailableToRead);
        }

        private void buttonStopTcpListener_Click(object sender, EventArgs e)
        {
            try
            {
                foreach (var key in mTcpClientDictionary.Keys)
                {
                    mTcpClientDictionary[key].TcpClient.Close();
                }

                mTcpClientDictionary.Clear();
                mTcpListener.Stop();                
            }
            catch
            {

            }
            finally
            {
                listBoxClientList.Items.Clear();
            }
        }

        private void buttonStartUdpListener_Click(object sender, EventArgs e)
        {
            mUdpClient = new UdpClient(Config.SocketServerPort);

            ThreadPool.QueueUserWorkItem((paramObj) => {
                EnableButton(buttonStartUdpListener, false);

                while (true)
                {
                    IPEndPoint remoteEndPoint = null;
                    byte[] receivedData = mUdpClient.Receive(ref remoteEndPoint);                    

                    string clientIP = remoteEndPoint.ToString();
                    if (!mUdpClientDictionary.ContainsKey(clientIP))
                    {
                        mUdpClientDictionary.Add(clientIP, DateTime.Now);
                    }

                    listBoxClientList.Invoke((Action)(() => {
                        foreach (string key in mUdpClientDictionary.Keys)
                        {
                            listBoxClientList.Items.Add(key + " " + mUdpClientDictionary[key].ToString("yyyy-MM-dd HH:mm:ss"));
                        }
                    }));

                    StringBuilder sb = new StringBuilder();

                    //receive
                    sb.Append(Encoding.UTF8.GetString(receivedData));

                    listBoxAddressList.Invoke((Action)(() => {
                        string now = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                        listBoxAddressList.Items.Add(remoteEndPoint.ToString() + " " + now + " : " + sb.ToString());
                    }));

                    //send
                    mUdpClient.Connect(remoteEndPoint);
                    byte[] dataToSend = Encoding.UTF8.GetBytes("Reply to: " + sb.ToString());
                    mUdpClient.Send(dataToSend, dataToSend.Length);
                }
            });            
        }

        private void buttonStopUdpListener_Click(object sender, EventArgs e)
        {
            try
            {
                if (mUdpClient != null)
                {
                    mUdpClient.Close();
                    mUdpClient = null;
                }
            }
            catch
            {
                
            }
        }

    }
}
